/*
 * FilesList.java
 *
 *  created: 13.10.2011
 *  charset: UTF-8
 *  license: MIT (X11) (See LICENSE file for full license)
 */
package cz.mp.k3bg.gui.component;

import cz.mp.k3bg.gui.helper.FileChooserBuilder;
import cz.mp.k3bg.log.LoggerManager;
import java.util.logging.Logger;
import cz.mp.k3bg.gui.MainFrame;
import cz.mp.util.GuiUtils;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.awt.event.KeyEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import cz.mp.k3bg.misc.ExtFileFilter;
import java.io.File;
import cz.mp.k3bg.util.DesktopUtils;
import javax.swing.JFileChooser;
import java.awt.event.ActionEvent;
import cz.mp.util.FileUtils;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultListModel;
import javax.swing.ListSelectionModel;
import cz.mp.k3bg.Images;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import static cz.mp.k3bg.Images.*;


/**
 * Komponenta {@code FileChooserField} pro pohodlnější výběr seznamu souborů.
 * 
 * @author Martin Pokorný
 * @version 0.1
 * @see FileChooserBuilder
 */
public class FilesList extends JPanel {

    private static final boolean DEBUG = false;
    private static final Logger logger =
            LoggerManager.getLogger(FilesList.class, DEBUG);
    
    private JList list = new JList();
    
    private JScrollPane scrollList = new JScrollPane(list);
    private JButton plusButton = new JButton("", Images.getImage(PLUS));
    private JButton minusButton = new JButton("", Images.getImage(MINUS));
    private JButton upButton = new JButton("", Images.getImage(ARROW_UP));
    private JButton downButton = new JButton("", Images.getImage(ARROW_DOWN));
    private JButton openFileButton = new JButton("", Images.getImage(Images.DETAIL));
    
    private FileChooserBuilder fileChooserBuilder;
    
    // -----
    
    /** */
    public FilesList() {
        super();
        
        fileChooserBuilder = new FileChooserBuilder();
        fileChooserBuilder.getFileChooser().setMultiSelectionEnabled(true);        
        
        openFileButton.setVisible(false);
        
        initListComponent();
        initLayout();
        initHandlers();    
    }
    
    /**
     * 
     */
    private void initListComponent() {
        DefaultListModel listmodel = new DefaultListModel();
        list = new JList(listmodel);       
        list.setSelectionMode (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);        
        list.setVisibleRowCount(-1);      // -1  = podle místa
        list.setSelectedIndex(-1);        // -1  = nic nevybráno
        
        scrollList = new JScrollPane(list);
        scrollList.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scrollList.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    }
    
    /**
     * 
     */
    private void initLayout() {
        this.setLayout(new MigLayout("",
                "0[fill,grow,75::max]rel[]0",
                "0[]0"));
        
        this.add(scrollList, "spany 4, growy");
        
        this.add(plusButton, "sgy, gapbottom rel");
        this.add(openFileButton, "sgy, gapbottom rel, wrap");
        this.add(minusButton, "sgy, wrap");
        this.add(upButton, "sgy, gapbottom rel, gaptop unrel, wrap");
        this.add(downButton, "sgy, wrap");        
    }

    /**
     * 
     */
    private void initHandlers() {
        plusButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                add();
                fireChangeEvent();
            }   
        });
        
        minusButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                remove();
                fireChangeEvent();
            }
        });
        
        upButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveUp();
                fireChangeEvent();
            }
        });        

        downButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveDown();
                fireChangeEvent();
            }
        });               
        
        openFileButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                openFile();
            }
        });
        
        list.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_DELETE ||
                        e.getKeyChar() == '-') {
                    remove();
                    fireChangeEvent();
                }
                else if (e.getKeyChar() == '+') {
                    add();
                    fireChangeEvent();
                }
                else if (e.isAltDown() && e.getKeyCode() == KeyEvent.VK_UP) {
                    moveUp();
                    fireChangeEvent();
                }
                else if (e.isAltDown() && e.getKeyCode() == KeyEvent.VK_DOWN) {
                    moveDown();
                    fireChangeEvent();
                }
                else if (e.getKeyCode() == KeyEvent.VK_F3) {
                    openFile();
                }                
            }
        });
                
        list.addListSelectionListener (new ListSelectionListener () {
            @Override
            public void valueChanged(ListSelectionEvent event)  {
                if (!event.getValueIsAdjusting()) {
                    int first = list.getSelectedIndex();
                    if (first >= 0) {
                        openFileButton.setVisible(true);
                    }                    
                    else {
                        openFileButton.setVisible(false);
                    }
                }
            }            
        } );        
    }
    
    /**
     * 
     */
    private void add() {
        JFileChooser fileChooser = fileChooserBuilder.getFileChooser();
        fileChooser.setSelectedFile(null);
        String rootDir = fileChooserBuilder.getRoot();
        DefaultListModel listModel = (DefaultListModel) list.getModel();

        if (fileChooser.showOpenDialog(null) != 
                JFileChooser.APPROVE_OPTION) {
            return;
        }

        logger.fine("rootDir = " + rootDir);
        
        File[] files = fileChooser.getSelectedFiles();
        for (File file : files) {
            String absPath = FileUtils.getAbsoluteFilePath(file);
            if (rootDir == null) {
                listModel.addElement(absPath);
            }
            else {
                logger.fine("absPath = " + absPath);
                String path = FileUtils.relativizePath(rootDir, absPath);
                logger.fine("path = " + path);
                if (! listModel.contains(path)) {
                    listModel.addElement(path);
                }
            }
        }        
    }
    
    /**
     * 
     */
    private void remove() {
        DefaultListModel listModel = (DefaultListModel) list.getModel();

        int[] selected = list.getSelectedIndices();
        for (int i=selected.length-1; i>=0; i--) {
            int idx = selected[i];
            listModel.remove(idx);
        }        
    }
    
    /**
     * 
     */
    private void moveUp() {
        DefaultListModel listModel = (DefaultListModel) list.getModel();

        int first = list.getSelectedIndex();
        if (first == -1 || first == 0) {
            return;
        }
        int[] selected = list.getSelectedIndices();
        
        int[] newSelected = new int[selected.length];
        for(int i=0; i<selected.length; i++) {
            int idx = selected[i];
            String temp = listModel.remove(idx).toString();
            listModel.add(idx-1, temp);                            
            newSelected[i] = idx-1;
        }
        list.setSelectedIndices(newSelected);
    
        Rectangle cb = list.getCellBounds(first-1, first-1);
        list.scrollRectToVisible(cb);                
    }

    /**
     * 
     */
    private void moveDown() {
        DefaultListModel listModel = (DefaultListModel) list.getModel();

        int first = list.getSelectedIndex();
        if (first == -1) {
            return;
        }
        int[] selected = list.getSelectedIndices();
        int last = selected[selected.length-1];
        if (last == listModel.size()-1) {
            return;
        }

        int[] newSelected = new int[selected.length];
        for (int i=selected.length-1; i>=0; i--) {
            int idx = selected[i];
            String temp = listModel.remove(idx).toString();
            listModel.add(idx+1, temp);                            
            newSelected[i] = idx+1;
        }
        list.setSelectedIndices(newSelected); 
        
        Rectangle cb = list.getCellBounds(last+1, last+1);
        list.scrollRectToVisible(cb);
    }


    /**
     * 
     */
    private void openFile() {
        String filePath = getSelectedFileAbsolutePath();
        if (filePath != null && openFileButton.isVisible()) {
            GuiUtils.setWaitCursorForAmoment(MainFrame.getInstance());
            DesktopUtils.openFile(filePath);
        }        
    }   

    /**
     * 
     * @param visible
     */
    public void setUpDownArrowButtonsVisible(boolean visible) {
        upButton.setVisible(visible);
        downButton.setVisible(visible);
    }

    /**
     * 
     * @return
     */
    public boolean isUpDownArrowButtonsVisible() {
        return upButton.isVisible() && downButton.isVisible();
    }

    /**
     * 
     * @return   {@code null}, pokud 
     */
    private String getSelectedFileAbsolutePath() {
        if (list.getSelectedIndex() < 0) {
            return null;
        }
        String path = list.getSelectedValue().toString();
        if (fileChooserBuilder.getRoot() == null || 
                path.startsWith(fileChooserBuilder.getRoot())) {
            return path;
        }
        else {
            return fileChooserBuilder.getRoot() + File.separator + path;
        }
    }
    

	/**
	 * Nastaví filtr definovaný seznamem přípon souboru a popisem filtru.
	 * 
	 * @param baseDescription
	 * @param exts
	 */
	public void setFilter(String baseDescription, String... exts) {
		ExtFileFilter filter = new ExtFileFilter(baseDescription, exts);
        setFilter(filter);                
	}
    
    /**
     * 
     * @param filter 
     */
    public void setFilter(ExtFileFilter filter) {
        fileChooserBuilder.setFilter(filter);
    }   
    
    /**
     * 
     */
    public void clear() {
        list.setModel(new DefaultListModel());        
    }

    /**
     * 
     * @param rootDirPath 
     */
    public void setRoot(String rootDirPath) {
        clear();
        fileChooserBuilder.setRoot(rootDirPath);
    }

    /**
     * 
     * @return 
     */
    public String getRoot() {
        return fileChooserBuilder.getRoot();
    }
    
    /**
     * 
     * @return
     */
    public ArrayList<String> getFiles() {
        ArrayList<String> result = new ArrayList<String>();
        int count = list.getModel().getSize();
        for(int i=0; i<count; i++) {
            String value = list.getModel().getElementAt(i).toString();
            result.add(value);
        }
        return result;
    }



    private boolean enabledInternal = true;

    @Override
    public void setEnabled(boolean enabled) {
        setOpaque(enabled);
        list.setOpaque(enabled);
        list.setEnabled(enabled);
        plusButton.setEnabled(enabled);
        minusButton.setEnabled(enabled);
        upButton.setEnabled(enabled);
        downButton.setEnabled(enabled);
        openFileButton.setEnabled(enabled);
        enabledInternal = enabled;
    }

    @Override
    public boolean isEnabled() {
        return enabledInternal;
    }

	// ----- ChangeEvent

	private EventListenerList changeListeners = new EventListenerList();

	/**
	 * Zaregistruje posluchače změny -- {@code ChangeEvent}.
	 * <em>poznámka:</em> událost {@code ChangeEvent} má být vystřelena při
	 * změně textu v poli s cestou k souboru nebo adresáři.
	 */
	public void addChangeListener(ChangeListener listener) {
		changeListeners.add(ChangeListener.class, listener);
	}

	public void removeChangeListener(ChangeListener listener) {
		changeListeners.remove(ChangeListener.class, listener);
	}

	private ChangeEvent changeEvent = new ChangeEvent(this);


	void fireChangeEvent() {
		fireChangeEvent(changeEvent);
	}

	/**
	 * Událost má být vystřelena při změně textu v poli s cestou k souboru.
	 *
	 * @param ed  (nemělo by být {@code null}!)
	 */
	private void fireChangeEvent(ChangeEvent ed) {
		Object[] listeners = changeListeners.getListenerList();
		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == ChangeListener.class) {
				((ChangeListener) listeners[i + 1]).stateChanged(ed);
			}
		}
	}
    
}   // FilesList.java
